/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package business;

import entity.Event;
import entity.Location;
import entity.Message;
import entity.MessageType;
import entity.Participant;
import entity.ParticipantPK;
import entity.SentMessage;
import entity.SentMessagePK;
import entity.User;
import entity.Weather;
import java.io.IOException;
import java.net.MalformedURLException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.json.JSONException;

/**
 * this bean is intended to handle alle the oprations on DB regarding the events
 *
 * @author Alessandro
 */
@Stateless
public class EventManager {

    @PersistenceContext
    EntityManager em;

    @Inject
    Principal principal;

    @EJB
    WeatherManager wm;

    /**
     * create on the DB an event
     *
     * @param event the event which gonna be saved
     * @param users the List of the invited users
     */
    public void create(Event event, List<String> users) {
        User creator = this.getLoggedUser();
        System.out.println(creator.getEmail());
        event.setCreator(creator);
        event.setDeleted(false);
        em.persist(event);
        event = em.find(Event.class, event.getId());
        System.out.println("questo e' il creatore " + event.getCreator().getEmail());
        this.addParticipantList(users, event.getId());
        this.sendMessages(users, event, MessageType.INVITED);
        em.flush();

        //If the event location is OUTDOOR we try to retrieve the weather forecast.
        //If the forecast is not available for that city or that date, the system saves a fake forecast and will update it. 
        if (event.getLocation().equals(Location.OUTDOOR)) {
            Weather weather = wm.createForecastFor(event);
            event.setForecast(weather);
            em.merge(event);
            em.flush();
        }
    }

    /**
     *
     * @return the user logged in this session
     */
    public User getLoggedUser() {
        User u = em.find(User.class, principal.getName());
        return em.find(User.class, principal.getName());
    }

    /**
     * create in the DB the relation btwn the invited users and the event
     *
     * @param users a list of user's email's
     * @param id event id
     */
    public void addParticipantList(List<String> users, long id) {
        if (users != null) {
            for (String u : users) {
                //User user=em.find(User.class, u);
                Participant p = new Participant();
                p.setAnswer("maybe");
                //p.setEvent1(em.find(Event.class, id));
                ///p.setUser1(user);
                //create a new primary key
                ParticipantPK pk = new ParticipantPK(id, u);
                p.setParticipantPK(pk);
                em.persist(p);
                System.out.println(p.getParticipantPK().toString());

            }

        }
        //add the creator to the participant
        Participant creatorPar = new Participant();
        creatorPar.setAnswer("yes");
        creatorPar.setParticipantPK(new ParticipantPK(id, getLoggedUser().getEmail()));
        em.persist(creatorPar);
        em.flush();

    }

    /**
     * get the event from the DB
     *
     * @param eventViewedId id of the searched event
     * @return the event itself
     */
    public Event findEvent(long eventViewedId) {
        em.flush();
        em.getEntityManagerFactory().getCache().evictAll();
        Event eventViewed = em.find(Event.class, eventViewedId);
        return eventViewed;
    }

    /**
     * checks if the creator is the logged user
     *
     * @param event an event
     * @return true if the creator is the logged user
     */
    public boolean checkCreator(Event event) {
        if (event.getCreator().equals(this.getLoggedUser())) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * check if the user is in the participant
     *
     * @param eventViewed an event
     * @return true if the user is invited and he/she is NOT the creator
     */
    public boolean checkParticipant(Event eventViewed) {
        List<User> participant = new ArrayList<>();

        for (Participant par : eventViewed.getParticipantCollection()) {
            participant.add(par.getUser1());
        }

        if (participant.contains(this.getLoggedUser()) && !(this.checkCreator(eventViewed))) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * modify the event either the fields (except for the title, cause it can't
     * be modified) and the invited list
     *
     * @param eventViewed the event to be modified
     * @param invited the invited list
     * @param oldInvited a pointer to the old lenght of the invited list
     */
    public void modify(Event eventViewed, List<String> invited, int oldInvited) {
        System.out.println("Sono event manager modifying");
        em.merge(eventViewed);
        em.flush();
        //Update the forecast:
        //If the event location is OUTDOOR we try to retrieve the weather forecast.
        //If the forecast is not available for that city or that date, the system saves a fake forecast and will update it. 
        if (eventViewed.getLocation().equals(Location.OUTDOOR)) {
            Weather weather = wm.createForecastFor(eventViewed);
            eventViewed.setForecast(weather);
            em.merge(eventViewed);
            em.flush();
        } else {
            eventViewed.setForecast(null);
            em.merge(eventViewed);
            em.flush();
        }
        
        List<String> invitedToAdvise = new ArrayList(invited); 
        invitedToAdvise = invitedToAdvise.subList(0, oldInvited);

        if (oldInvited != 0) {
            //Send messages to old invited people about the modification
            //Create a list of old invited except of the creator
            invitedToAdvise.remove(eventViewed.getCreator().getEmail());
            //Removing from the list of people to advise all the decliner:
            for (Participant p : eventViewed.getParticipantCollection()) {
                if (p.getAnswer().equals("no")) {
                    invitedToAdvise.remove(p.getUser1().getEmail());
                }
            }
            this.sendMessages(invitedToAdvise, eventViewed, MessageType.MODIFIED);
        }

        if (invited.size() > oldInvited) {

            //nuovi invitati
            List<String> newInv = invited.subList(oldInvited, invited.size());
            System.out.println("new inv: " + newInv.toString());

            for (String u : newInv) {
                //invito nuovi participant
                Participant p = new Participant();
                p.setAnswer("maybe");
                ParticipantPK pk = new ParticipantPK(eventViewed.getId(), u);
                p.setParticipantPK(pk);
                em.persist(p);
                System.out.println(p.getParticipantPK().toString());
            }
            //Send messages of new invitation for the event

            this.sendMessages(newInv, eventViewed, MessageType.INVITED);
            em.flush();
        }

    }

    public void deleteEvent(Event eventViewed) {
        System.out.println("sono eventmanager cancellando");
        eventViewed.setDeleted(true);
        em.merge(eventViewed);
        em.flush();
        System.out.println("deleted = " + eventViewed.isDeleted());
        //Send messages of deletion of the event
        //Create new list of people to advise: the invited people except of the creator
        List<String> peopleToAdvise = new ArrayList();
        for (Participant p : eventViewed.getParticipantCollection()) {
            if (!p.getUser1().equals(eventViewed.getCreator()) && !(p.getAnswer().equals("no"))) {
                peopleToAdvise.add(p.getUser1().getEmail());
            }
        }
        this.sendMessages(peopleToAdvise, eventViewed, MessageType.DELETED);
    }

    public void joinInvitation(Event eventViewed) {
        User currentUser = this.getLoggedUser();
        for (Participant par : eventViewed.getParticipantCollection()) {
            if (par.getUser1().equals(currentUser)) {
                par.setAnswer("yes");
                em.merge(par);
                em.flush();
            }
        }
    }

    public void declineInvitation(Event eventViewed) {
        User currentUser = this.getLoggedUser();
        for (Participant par : eventViewed.getParticipantCollection()) {
            if (par.getUser1().equals(currentUser)) {
                par.setAnswer("no");
                em.merge(par);
                em.flush();

            }
        }
    }

    /**
     * The function invoked to create messages and create the list of users that
     * have to receive them
     *
     * @param users the list of the emails of receivers
     * @param event the event that has to be notified
     * @param messageType the tipe of the message
     */
    public void sendMessages(List<String> users, Event event, MessageType messageType) {
        //create a message and set its fields
        Message message = new Message();
        message.setEvent(event);
        message.setType(messageType);
        message.setTimestamp(Calendar.getInstance().getTime());
        em.persist(message);
        em.flush();

        //create the sentMessages
        int idMessage = em.find(Message.class, message.getMessageId()).getMessageId();
        if (users != null) {
            for (String u : users) {
                //create the key
                SentMessagePK smPK = new SentMessagePK();
                smPK.setUser(u);
                smPK.setMessage(idMessage);
                //create the sentMessage
                SentMessage sm = new SentMessage(smPK);
                sm.setPending(true);
                em.persist(sm);
                System.out.println("manadato a: " + sm.toString());
                em.flush();
            }
            em.flush();
        }
    }
}
